package com.hoshiicloud.payment.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.hoshiicloud.common.utils.DigestUtils;
import com.hoshiicloud.payment.config.RsaConfig;
import com.hoshiicloud.payment.vo.EncryptedResponse;
import com.hoshiicloud.payment.util.aesrsa.AES;
import com.hoshiicloud.payment.util.aesrsa.ConvertUtils;
import com.hoshiicloud.payment.util.aesrsa.EncryUtil;
import com.hoshiicloud.payment.util.aesrsa.RSA;
import com.hoshiicloud.payment.util.aesrsa.SecureRandomUtil;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.TreeMap;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
 * @author Mr.wu
 */
@Slf4j
public class TensoonUtil {

    public static String base64_decode_8859(final String source) {
        String result = "";

        final java.util.Base64.Decoder decoder = java.util.Base64.getDecoder();
        try {
            result = new String(decoder.decode(source), "ISO-8859-1");
            //此处的字符集是ISO-8859-1
        } catch (final UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return result;
    }

    public static String decryptData(final String base64Data, byte[] key) throws Exception {
        AES.initialize();
        final Cipher cipher = Cipher.getInstance(AES.ALGORITHM, "BC");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); //生成加密解密需要的Key
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        return new String(cipher.doFinal(base64_decode_8859(base64Data).getBytes("ISO-8859-1")),
                "utf-8");
        //注意:第一个是ISO-8859-1,第二个是utf-8
    }

    /**
     * <p>
     *
     * @param response 客户端
     * @param str 需要回写的字符串
     * @Title: responseWriteString
     * </p>
     * <p>
     * @Description: 往客户端回写字符串数据
     * </p>
     */
    public static void responseWriteString(HttpServletResponse response, String str) {
        PrintWriter out = null;
        try {
            response.setContentType("text/html;charset=utf-8");
            out = response.getWriter();
            out.write(str);
            out.flush();
        } catch (IOException e) {
            log.error("往客户端回写字符串数据失败，失败信息：{}", str, e);
        } finally {
            if (null != out) {
                out.close();
            }
        }
    }

    public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) throws Exception {
        if (map == null) {
            return null;
        }

        Object obj = beanClass.newInstance();

        org.apache.commons.beanutils.BeanUtils.populate(obj, map);

        return obj;
    }

    public static Map<?, ?> objectToMap(Object obj) {
        if (obj == null) {
            return null;
        }

        return new org.apache.commons.beanutils.BeanMap(obj);
    }

    /**
     * @param param
     * @param key
     * @return java.lang.String
     * @author: Mr.wu
     * @descripton: 生成md5签名字符串
     * @date: 16:30 2019/5/27
     */
    public static String sign(TreeMap<String, String> param, String key) {

        StringBuffer paramstr = new StringBuffer();

        for (String pkey : param.keySet()) {
            String pvalue = String.valueOf(param.get(pkey));
            // 空值不传递，不签名
            if (StringUtils.isNotBlank(pvalue) && !"sign".equals(pkey)) {
                // 签名原串，不url编码
                paramstr.append(pkey + "=" + pvalue + "&");
            }
        }
        String paramSrc = paramstr.substring(0, paramstr.length() - 1);

        log.info(paramSrc);
        String sign = DigestUtils.md5(paramSrc + "&key=" + key);
        return sign;

    }

    /**
     * @param param
     * @param key
     * @return boolean
     * @author: Mr.wu
     * @descripton: md5签名验证
     * @date: 16:31 2019/5/27
     */
    public static boolean checkSign(TreeMap<String, String> param, String key) {
        String sign = sign(param, key);
        log.info("sign:" + sign);
        if (sign.equals(param.get("sign"))) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param param
     * @return com.hoshiicloud.payment.vo.EncryptedResponse
     * @author: Mr.wu
     * @descripton:使用aes+rsa加密数据,返回加密数据实体,需先配置rsa密钥
     * @date: 17:50 2019/5/27
     */
    public static EncryptedResponse encryptData(Object param) throws Exception {
        TreeMap<String, Object> paramMap = JSONObject.parseObject(JSON.toJSONString(param),
                new TypeReference<TreeMap<String, Object>>() {
                });
        // 生成RSA签名
        String sign = EncryUtil.handleRSA(paramMap, RsaConfig.getClientPrivateKey());
        paramMap.put("sign", sign);

        String info = JSON.toJSONString(paramMap);
        //随机生成AES密钥
        String aesKey = SecureRandomUtil.getRandom(16);
        //AES加密数据
        String data = AES.encryptToBase64(ConvertUtils.stringToHexString(info), aesKey);

        // 使用RSA算法将商户自己随机生成的AESkey加密
        String encryptkey = RSA.encrypt(aesKey, RsaConfig.getServerPublicKey());
        EncryptedResponse encryptedResponse = new EncryptedResponse();
        encryptedResponse.setData(data);
        encryptedResponse.setEncryptKey(encryptkey);
        log.debug("加密后的请求数据:" + encryptedResponse.toString());
        return encryptedResponse;
    }

    /**
     * @param jsonCipher 请求体json字符串
     * @return java.util.Map<java.lang.String, java.lang.String>
     * @author: Mr.wu
     * @descripton: 解密使用aes+rsa加密的数据,返回数据map,需先配置rsa密钥,如验签失败返回空
     * @date: 15:42 2019/6/1
     */
    public static Map<String, String> decryptData(String jsonCipher) throws Exception {
        EncryptedResponse req = JSON.parseObject(jsonCipher, EncryptedResponse.class);
        // 验签
        boolean passSign = EncryUtil.checkDecryptAndSign(req.getData(),
                req.getEncryptKey(), RsaConfig.getClientPublicKey(),
                RsaConfig.getServerPrivateKey());

        if (passSign) {
            // 验签通过
            String aeskey = RSA.decrypt(req.getEncryptKey(),
                    RsaConfig.getServerPrivateKey());
            String data = ConvertUtils.hexStringToString(AES.decryptFromBase64(req.getData(),
                    aeskey));
            log.info("解密后的明文:" + data);
            Map<String, String> dataMap = JSON.parseObject(data, Map.class);
            return dataMap;
        } else {
            log.info("验签失败");
        }
        return null;
    }


}
